﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Windows.Media;

using Styx;
using Styx.Plugins;
using Styx.Common;
using Styx.CommonBot;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;


namespace PlayerObject
{
    public class Player
    {
        public WoWPlayer objPlayer { get; set; }
        public DateTime FirstSeen { get; set; }
        public TimeSpan SinceFirstSeen { get; set; }
        public DateTime LastSeen { get; set; }
        public DateTime TargetedAt { get; set; }
        public TimeSpan SinceTarget { get; set; }
        public int DistanceSqr { get; set; }
        public int LastFollowNotified { get; set; }
        public int LastTargetNotified { get; set; }
    }
}

namespace GatherSafe
{



    public class GatherSafe : HBPlugin
    {
        public override string Name { get { return "GatherSafe"; } }
        public override string Author { get { return "Saik"; } }
        public override Version Version { get { return new Version(1, 0); } }
        public override bool WantButton { get { return true; } }
        List<PlayerObject.Player> playerslist = new List<PlayerObject.Player>();
        List<WoWGameObject> objectslist = new List<WoWGameObject>();
        bool bStarted = false;
        DateTime PluginStarted = new DateTime();
        TimeSpan PluginTime = new TimeSpan();
        
        public override void Initialize()
        {
            Logging.Write(LogLevel.Normal, ColorSelection((int)MIRsettings.Instance.NotificationColor), "GatherSafe v" + Version.ToString() + " - Loaded");
        }

        public override void OnButtonPress()
        {
            new Form1().ShowDialog();
        }

        public override void Pulse()
        {
            if (StyxWoW.IsInGame)
            {
                if (StyxWoW.Me.IsAlive && (StyxWoW.Me.Mounted || MIRsettings.Instance.ScanUnmounted))  // You have to be alive and either mounted or have scanunmounted enabled
                {

                    TimeLogoutCheck();

                    PlayerDetection();  //Avoids players

                    if (MIRsettings.Instance.NodeBlacklist) 
                    {
                    List<WoWGameObject> Objects = NodeBlacklist();  //Avoids nodes
                    }

                }
            }
        }

        private void TimeLogoutCheck()
        {

            //Only do this if enabled

            if (MIRsettings.Instance.TimedLogout)
            {
                if (bStarted)
                {
                    // Set the timespan - check to see if the timespan > setting to logout - if so, log out

                    PluginTime = (DateTime.Now - PluginStarted);

                    if (PluginTime.TotalMinutes >= MIRsettings.Instance.LogoutTime)  // If you've been logged in longer than your setting
                    {

                        Logging.Write(LogLevel.Normal, ColorSelection(MIRsettings.Instance.NotificationColor), "[GatherSafe]: You are being logged out due to your GatherSafe logout timer.");

                        InactivityDetector.ForceLogout(true);

                    }

                }
                else
                {
                    // Set the StartedPlugin Date time, also set bstarted to true

                    PluginStarted = DateTime.Now;
                        bStarted = true;

                }
            }

            
        }



        public System.Windows.Media.Color ColorSelection(int choice) {

            switch (choice)
            {
                case 0:
                    return System.Windows.Media.Colors.MediumSlateBlue;
                case 1:
                    return System.Windows.Media.Colors.Blue;
                case 2:
                    return System.Windows.Media.Colors.Green;
                case 3:
                    return System.Windows.Media.Colors.Red;
                case 4:
                    return System.Windows.Media.Colors.Orange;
                case 5:
                    return System.Windows.Media.Colors.Purple;
                case 6:
                    return System.Windows.Media.Colors.White;
                default:
                    return System.Windows.Media.Colors.MediumSlateBlue;
            }
        }


        public void PlayerDetection()
        {

            #region Setup Player Search

            int PlayerSearchRange = 0;
            bool skipplayersentirely = false;

            if (MIRsettings.Instance.PlayerDetection && MIRsettings.Instance.NodePlayerBlacklist)
            {
                if (MIRsettings.Instance.PlayerRange > MIRsettings.Instance.PlayerDistance)  //Always use the farthest search method so that it is gauranteed that it will work
                {
                    PlayerSearchRange = MIRsettings.Instance.PlayerRange;
                }
                else
                {
                    PlayerSearchRange = MIRsettings.Instance.PlayerDistance;
                }
            }
            else if (MIRsettings.Instance.PlayerDetection)
            {
                PlayerSearchRange = MIRsettings.Instance.PlayerRange;
            }
            else if (MIRsettings.Instance.NodePlayerBlacklist)
            {
                PlayerSearchRange = MIRsettings.Instance.PlayerDistance;
            }
            else
            {
                skipplayersentirely = true;
            }

            #endregion Setup Player Search

            if (!skipplayersentirely)
            {

                foreach (PlayerObject.Player tst in playerslist.Where(x => DateTime.Now > x.LastSeen.AddSeconds(MIRsettings.Instance.ClearListTime)).ToList())
                {
                    Logging.Write(LogLevel.Diagnostic, Colors.Red, tst.objPlayer.Name + " has been removed from the list.  Now = " + DateTime.Now.ToLongTimeString() + " >= " + tst.LastSeen.AddSeconds(MIRsettings.Instance.ClearListTime).ToLongTimeString());
                }

                playerslist = playerslist.Where(x => DateTime.Now < x.LastSeen.AddSeconds(MIRsettings.Instance.ClearListTime)).ToList();

                var playerList = ObjectManager.GetObjectsOfType<WoWPlayer>(false, false).Where(o => o.IsPlayer && o.Distance < MIRsettings.Instance.PlayerRange).ToList();
                foreach (var woWPlayer in playerList.Where(woWPlayer => woWPlayer != null))
                {

                    PlayerObject.Player newplayer = new PlayerObject.Player();
                    newplayer.objPlayer = woWPlayer;
                    newplayer.FirstSeen = DateTime.Now;
                    newplayer.LastSeen = DateTime.Now;
                    newplayer.SinceFirstSeen = new TimeSpan();
                    newplayer.DistanceSqr = (int)woWPlayer.DistanceSqr;
                    newplayer.LastFollowNotified = -1;
                    newplayer.LastTargetNotified = -1;

                    if (woWPlayer.IsTargetingMeOrPet)
                    {
                        newplayer.TargetedAt = DateTime.Now;
                        newplayer.SinceTarget = new TimeSpan();
                    }



                    int count = 0;
                    DateTime lastseen = DateTime.Now;
                    DateTime TargetedAt = DateTime.Now;
                    DateTime firstseen = DateTime.Now;
                    TimeSpan SinceFirstSeen = new TimeSpan();
                    TimeSpan SinceTarget = new TimeSpan();
                    int LastFollowNotified = -1;
                    int LastTargetNotified = -1;
                    int DistanceSqr = 0;
                    int countupdate = 0;
                    bool skip = false;
                    bool istargeting = false;

                    if (playerslist.Any(x => x.objPlayer.Name == woWPlayer.Name)) //Is the player in our list of already seen players?
                    {//Yes

                        foreach (PlayerObject.Player tpl in playerslist)
                        {//Our list of already seen players

                            if (tpl.objPlayer.Name == woWPlayer.Name)  //Is this player from the list the current player?
                            { // Yes - get data regarding this player

                                lastseen = tpl.LastSeen;
                                TargetedAt = tpl.TargetedAt;
                                firstseen = tpl.FirstSeen;
                                SinceFirstSeen = tpl.SinceFirstSeen;
                                DistanceSqr = tpl.DistanceSqr;
                                istargeting = woWPlayer.IsTargetingMeOrPet;
                                LastFollowNotified = tpl.LastFollowNotified;
                                LastTargetNotified = tpl.LastTargetNotified;

                                if (istargeting)
                                {
                                    TargetedAt = tpl.TargetedAt;
                                    SinceTarget = tpl.SinceTarget;
                                }

                            }
                            else
                            {
                                //Skip removal below by setting attribute
                                skip = true;
                            }

                        }

                        if (skip)
                        {
                            PlayerObject.Player existingplayer = new PlayerObject.Player();
                            existingplayer.objPlayer = woWPlayer;
                            existingplayer.LastSeen = lastseen;
                            existingplayer.FirstSeen = firstseen;
                            existingplayer.SinceFirstSeen = SinceFirstSeen;
                            existingplayer.DistanceSqr = DistanceSqr;
                            existingplayer.LastFollowNotified = LastFollowNotified;
                            existingplayer.LastTargetNotified = LastTargetNotified;
                            if (TargetedAt != null)
                            {
                                existingplayer.TargetedAt = TargetedAt;
                                existingplayer.SinceTarget = SinceTarget;
                            }

                            PlayerObject.Player existingplayerupdate = new PlayerObject.Player();
                            existingplayerupdate.objPlayer = woWPlayer;
                            existingplayerupdate.LastSeen = DateTime.Now;
                            existingplayerupdate.FirstSeen = firstseen;
                            existingplayerupdate.SinceFirstSeen = (DateTime.Now - firstseen);
                            existingplayerupdate.DistanceSqr = (int)woWPlayer.DistanceSqr;
                            existingplayerupdate.LastFollowNotified = LastFollowNotified;
                            existingplayerupdate.LastTargetNotified = LastTargetNotified;
                            if (TargetedAt != null)  // update targeting information if previously targeted
                            {
                                existingplayerupdate.TargetedAt = TargetedAt;
                                existingplayerupdate.SinceTarget = (DateTime.Now - TargetedAt);
                            }

                            //Player is targeting me?
                            if (woWPlayer.IsTargetingMeOrPet)
                            {
                                //Was I targeted before?
                                if (TargetedAt != null)
                                {
                                    existingplayer.TargetedAt = TargetedAt;
                                }
                                else  //Have not been targeted before
                                {
                                    existingplayerupdate.TargetedAt = DateTime.Now;
                                }
                            }



                            #region Player Detection
                            // PLAYER NOTIFICATIONS - DETECTION SECTION
                            if (MIRsettings.Instance.PlayerDetection) //Player detection
                            {

                                if (MIRsettings.Instance.TargetNotifications || MIRsettings.Instance.DCTarget)
                                {
                                    // REPLACE 3 WITH EVERY X SECONDS SETTING
                                    if ((int)existingplayerupdate.SinceFirstSeen.TotalSeconds % MIRsettings.Instance.TargetNotificationsTime == 0 && (int)existingplayerupdate.SinceFirstSeen.TotalSeconds > MIRsettings.Instance.TargetNotificationsTimeStartAfter)
                                    {
                                        if (existingplayerupdate.objPlayer.IsTargetingMeOrPet)
                                        {//Not only is player following you, but is targeting you as well

                                            if (MIRsettings.Instance.TargetNotifications)
                                            {
                                                if (existingplayerupdate.LastTargetNotified < ((int)existingplayerupdate.SinceFirstSeen.TotalSeconds))
                                                {
                                                    //have not been notified for this amount of time yet - lets notify!
                                                    Logging.Write(LogLevel.Normal, ColorSelection(MIRsettings.Instance.NotificationColor), "[GatherSafe]: " + woWPlayer.Name.ToString() + " has been targeting you for " + (((int)existingplayerupdate.SinceTarget.TotalSeconds).ToString()) + " seconds!");
                                                    existingplayerupdate.LastTargetNotified = (int)existingplayerupdate.SinceTarget.TotalSeconds;
                                                }
                                            }

                                            if (MIRsettings.Instance.DCTarget)
                                            {
                                                if (existingplayerupdate.SinceFirstSeen.TotalSeconds >= MIRsettings.Instance.DCTargetTime)
                                                {
                                                    Logging.Write(LogLevel.Normal, ColorSelection(MIRsettings.Instance.NotificationColor), "[GatherSafe]: You are being logged out because you were targeted for too long.  You specified to log out after being followed for " + MIRsettings.Instance.DCTargetTime.ToString() + " seconds.");
                                                    InactivityDetector.ForceLogout(true);
                                                }
                                            }



                                        }
                                    }
                                }


                                if (MIRsettings.Instance.FollowNotifications || MIRsettings.Instance.DCFollow)
                                {
                                    // REPLACE 3 WITH EVERY X SECONDS SETTING
                                    if ((int)existingplayerupdate.SinceFirstSeen.TotalSeconds % MIRsettings.Instance.FollowNotificationsTime == 0 && (int)existingplayerupdate.SinceFirstSeen.TotalSeconds > MIRsettings.Instance.FollowNotificationsTimeStartAfter)  // Checks every x seconds, starting at x seconds
                                    {

                                        //This player existed last several pulses

                                        if (!existingplayerupdate.objPlayer.IsTargetingMeOrPet)
                                        {//Player has been within range often, but you are not targeted

                                            if (MIRsettings.Instance.FollowNotifications)
                                            {
                                                if (existingplayerupdate.LastFollowNotified < ((int)existingplayerupdate.SinceFirstSeen.TotalSeconds))
                                                {
                                                    //Haven't been notified this second, lets notify!
                                                    Logging.Write(LogLevel.Normal, ColorSelection(MIRsettings.Instance.NotificationColor), "[GatherSafe]: " + woWPlayer.Name.ToString() + " has been following you for " + (((int)existingplayerupdate.SinceFirstSeen.TotalSeconds).ToString()) + " seconds!");
                                                    existingplayerupdate.LastFollowNotified = (int)existingplayerupdate.SinceFirstSeen.TotalSeconds;
                                                }
                                            }

                                            if (MIRsettings.Instance.DCFollow)
                                            {
                                                if (existingplayerupdate.SinceFirstSeen.TotalSeconds >= MIRsettings.Instance.DCFollowTime)
                                                {
                                                    Logging.Write(LogLevel.Normal, ColorSelection(MIRsettings.Instance.NotificationColor), "[GatherSafe]: You are being logged out because you were followed for too long.  You specified to log out after being followed for " + MIRsettings.Instance.DCFollowTime.ToString() + " seconds.");
                                                    InactivityDetector.ForceLogout(true);
                                                }
                                            }

                                        }


                                    }
                                }
                                
                            }


                            #endregion Player Detection





                            //Removes previous entry
                            playerslist = playerslist.Where(x => x.objPlayer.Name != existingplayer.objPlayer.Name).ToList();

                            //Adds updated player
                            playerslist.Add(existingplayerupdate);



                        }

                    }
                    else
                    {//Not already seen at all
                        playerslist.Add(newplayer);
                    }


		            
                }

            }

        }

        public List<WoWGameObject> NodeBlacklist()
        {



            var objectList = (from o in ObjectManager.GetObjectsOfType<WoWGameObject>(false, false)
                              where (o.IsHerb || o.IsMineral)
                              orderby o.DistanceSqr ascending
                              select o).ToList();

                //check if  objects have mobs nearby, if so, blacklist the node for X seconds
                foreach (WoWGameObject objHerbMin in objectList)
                {


                    if (MIRsettings.Instance.NodeMobBlacklist)
                    {
                        var moblist = ObjectManager.GetObjectsOfType<WoWUnit>(false, false).Where(o => (o.IsAlive && o.IsHostile) && o.Location.DistanceSqr(objHerbMin.Location) < (MIRsettings.Instance.MobDistance * MIRsettings.Instance.MobDistance)).ToList();
                        
                        //If mobs are within mobdistance of node, blacklist the node
                        if (moblist.Any(mob => mob != null))
                        {
                            if (moblist.Count >= MIRsettings.Instance.MobCount)
                            {
                                if (!Blacklist.Contains(objHerbMin, BlacklistFlags.Node))
                                {
                                    Blacklist.Add(objHerbMin, BlacklistFlags.Node, TimeSpan.FromSeconds(MIRsettings.Instance.BlacklistTime));
                                    Logging.Write(LogLevel.Normal, ColorSelection(MIRsettings.Instance.NotificationColor), "[GatherSafe]: Blacklisted node " + objHerbMin.Name + " " + ((int)objHerbMin.Distance).ToString() + " units away for " + MIRsettings.Instance.BlacklistTime.ToString() + " seconds with " + moblist.Count.ToString() + " mobs within range of " + MIRsettings.Instance.MobDistance.ToString());
                                }
                            }

                        }
                    }

                    if (MIRsettings.Instance.NodePlayerBlacklist)
                    {
                        if (playerslist.Count > 0)
                        {
                            List<PlayerObject.Player> avoidplayers = playerslist.Where(o => o.DistanceSqr < (MIRsettings.Instance.PlayerDistance * MIRsettings.Instance.PlayerDistance)).ToList();

                            //If players are within player range distance, blacklist the node
                            if (avoidplayers.Any(x => x != null))
                            {

                                if (MIRsettings.Instance.PlayerCloseToNode)//DO NOT BLACKLIST UNLESS YOU ARE FAR ENOUGH AWAY FROM NODE
                                {
                                    if (objHerbMin.DistanceSqr > (MIRsettings.Instance.PlayerCloseToNodeRange * MIRsettings.Instance.PlayerCloseToNodeRange)) //Greater than specified distance
                                    {
                                        if (!Blacklist.Contains(objHerbMin, BlacklistFlags.Node))
                                        {
                                            Blacklist.Add(objHerbMin, BlacklistFlags.Node, TimeSpan.FromSeconds(MIRsettings.Instance.BlacklistTime));
                                            Logging.Write(LogLevel.Normal, ColorSelection(MIRsettings.Instance.NotificationColor), "[GatherSafe]: Blacklisted node " + objHerbMin.Name + " " + ((int)objHerbMin.Distance).ToString() + " units away for " + MIRsettings.Instance.BlacklistTime.ToString() + " seconds with " + avoidplayers.Count().ToString() + " players within range of " + MIRsettings.Instance.PlayerDistance.ToString());
                                            foreach (PlayerObject.Player tst in avoidplayers)
                                            {
                                                Logging.Write(LogLevel.Diagnostic, Colors.Red, tst.objPlayer.Name + " was in range of the blacklisted node");
                                            }
                                        }
                                    } // NO ELSE
                                }
                                else
                                {
                                    if (!Blacklist.Contains(objHerbMin, BlacklistFlags.Node))
                                    {
                                        Blacklist.Add(objHerbMin, BlacklistFlags.Node, TimeSpan.FromSeconds(MIRsettings.Instance.BlacklistTime));
                                        Logging.Write(LogLevel.Normal, ColorSelection(MIRsettings.Instance.NotificationColor), "[GatherSafe]: Blacklisted node " + objHerbMin.Name + " " + ((int)objHerbMin.Distance).ToString() + " units away for " + MIRsettings.Instance.BlacklistTime.ToString() + " seconds with " + avoidplayers.Count().ToString() + " players within range of " + MIRsettings.Instance.PlayerDistance.ToString());
                                        foreach (PlayerObject.Player tst in avoidplayers)
                                        {
                                            Logging.Write(LogLevel.Diagnostic, Colors.Red, tst.objPlayer.Name + " was in range of the blacklisted node");
                                        }
                                    }
                                }
                                
                            }
                        }

                        
                    }

            }
                return objectList;
        }
    }
}